/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sane;

import java.util.Arrays;
import uk.ac.bath.util.MyRandom;

/**
 *
 * @author pjl
 */
public class Population {

    ProgressViewer view;

    Gene genes[];
    private Context c;
    private Gene[] net_pop;
    int evaluations = 0;
    float best;
    int generation;

    Population(Context c,ProgressViewer view) {
        this.c = c;
        this.view=view;

        genes = new Gene[c.nPop];
        for (int i = 0; i < c.nPop; i++) {
            genes[i] = new Gene(c.bitsPerGene, true);
        }
        net_pop = new Gene[c.ZETA];
        this.best = 0.0f;
    }

    Network createRandomNet() {
        /*find random subpopulation*/
        for (int j = 0; j < c.ZETA; ++j) {
            net_pop[j] = genes[MyRandom.nextInt(c.nPop)];
            net_pop[j].tests++;
        }

        Network net = Builder.build(net_pop, c);    /*form the network*/
        return net;
    }

    private float eval() {


        for (Gene g : genes) {
            g.fitness = 0.0f;
            g.tests = 0;

        }


        for (int i = 0; i < c.NUM_TRIALS; ++i) {


            float fitness = 0;
            Network net = createRandomNet();
            ++evaluations;

            fitness=Float.MAX_VALUE;
            for (int k = 0; k < 50; k++) {
                /*use network to balance the pole*/
                CartSim sim = new CartSim();
                float fitTmp = sim.run(net, c.MAX_STEPS, true);
                fitness=Math.min(fitness,fitTmp);

                if (fitness < best) {
                    break;
                }                
            }

            if (fitness > best) {
                best = fitness;
                view.setNet(net);

            }

            if (fitness >= c.MAX_STEPS) {
                System.out.println("Got It! in " + evaluations + " evaluations.\n");
                return fitness;
            }

            /* add fitness to each participating neuron's fitness variable*/
            for (int j = 0; j < c.ZETA; ++j) {
                net_pop[j].fitness += fitness;
            }
        }

        /*get average fitness level.  Multiply by 100 to get more resolution*/

        for (int i = 0; i < c.nPop; ++i) {
            if (genes[i].tests > 0) {
                genes[i].fitness = (float) ((genes[i].fitness * 100.0) / genes[i].tests);
            } else {
                genes[i].fitness = 0;
            }
        }

        System.out.print(String.format("generation %d, %f steps\n", generation, best));
        return best;
    }

    /**
     *
     * - evaluate current genes.
     * - breed ready for next step
     *
     * @return  true if champion is found.
     */
    boolean step() {
        generation++;
        if (eval() >= c.MAX_STEPS) {
            return true;
        }
        breed();
        return false;
    }

    private void breed() {

        Arrays.sort(genes, Gene.rev);

        for (int i = 0; i < c.NUM_BREED; i++) {
            Gene a = genes[i];
            int nn = i;
            if (i == 0) {
                nn = c.NUM_BREED;
            }
            Gene b = genes[MyRandom.nextInt(nn)];

            int point = MyRandom.nextInt(c.bitsPerGene + 1);

            Gene a1 = genes[c.nPop - 2 * i - 1];
            Gene b1 = genes[c.nPop - 2 * i - 2];

            for (int j = 0; j < c.bitsPerGene; j++) {
                if (j < point) {
                    a1.bits[j] = a.bits[j];
                    b1.bits[j] = b.bits[j];
                } else {
                    a1.bits[j] = b.bits[j];
                    b1.bits[j] = a.bits[j];
                }
            }
        }

        for (int i = c.NUM_BREED; i < c.nOut; i++) {
            int point = MyRandom.nextInt(c.bitsPerGene);
            genes[i].bits[point] = !genes[i].bits[point];
        }

    }
}


